package cn.apelx.shardingjdbc.java_api;

import com.alibaba.druid.pool.DruidDataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.shardingsphere.driver.api.ShardingSphereDataSourceFactory;
import org.apache.shardingsphere.encrypt.api.config.EncryptRuleConfiguration;
import org.apache.shardingsphere.encrypt.api.config.rule.EncryptColumnRuleConfiguration;
import org.apache.shardingsphere.encrypt.api.config.rule.EncryptTableRuleConfiguration;
import org.apache.shardingsphere.infra.config.RuleConfiguration;
import org.apache.shardingsphere.infra.config.algorithm.ShardingSphereAlgorithmConfiguration;
import org.apache.shardingsphere.infra.config.mode.ModeConfiguration;
import org.apache.shardingsphere.mode.repository.cluster.ClusterPersistRepositoryConfiguration;
import org.apache.shardingsphere.mode.repository.standalone.StandalonePersistRepositoryConfiguration;
import org.apache.shardingsphere.readwritesplitting.api.ReadwriteSplittingRuleConfiguration;
import org.apache.shardingsphere.readwritesplitting.api.rule.ReadwriteSplittingDataSourceRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.keygen.KeyGenerateStrategyConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.sharding.StandardShardingStrategyConfiguration;
import org.bouncycastle.math.raw.Mod;

import javax.sql.DataSource;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;

/**
 * java api
 *
 * @author apelx
 * @since 2022-05-13
 */
public class ShardingJdbcJavaApi {

    public static void main(String[] args) throws SQLException {


        String schemaName = "foo_schema"; // 指定逻辑 Schema 名称
        ModeConfiguration modeConfig = buildMode(); // 构建运行模式
        Map<String, DataSource> dataSourceMap = buildDataSource(); // 构建真实数据源
        Collection<RuleConfiguration> ruleConfigs = buildRule(); // 构建具体规则
        Properties otherProperties = new Properties();
        otherProperties.setProperty("sql-show", "true"); // 构建属性配置
        DataSource shardingDataSource = ShardingSphereDataSourceFactory.createDataSource(schemaName, modeConfig, dataSourceMap, ruleConfigs, otherProperties);

        Connection connection = shardingDataSource.getConnection();

        PreparedStatement preparedStatement = connection.prepareStatement("insert into t_order(id, order_type, customer_id, amount) values(?, ?, ?, ?)");

        Random random = new Random();
        for (int i = 1; i <= 10; i++) {
            preparedStatement.setInt(1, i);
            preparedStatement.setInt(2, i);
            preparedStatement.setInt(3, random.nextInt(10 ));
            preparedStatement.setBigDecimal(4, new BigDecimal(i * 100));
            preparedStatement.execute();
        }
        connection.close();


    }

    private static ModeConfiguration buildMode() {

        /**-----------------------单机节点配置Standalone------------------------**/
        Properties standalonePro = new Properties();
        // 元数据存储路径 默认值.shardingsphere
        standalonePro.put("path", ".shardingsphere");
        // type: 只有file
        StandalonePersistRepositoryConfiguration standaloneConfig = new StandalonePersistRepositoryConfiguration("File", standalonePro);
        /**--------------------集群配置 - zookeeper-----------------------**/
       /* Properties zkPro = new Properties();
        // 重试间隔毫秒数 默认值500
        zkPro.put("retryIntervalMilliseconds", 500);
        // 客户端连接最大重试次数 默认值3
        zkPro.put("maxRetries", 3);
        // 临时数据失效的秒数 默认60
        zkPro.put("timeToLiveSeconds", 60);
        // 客户端操作超时的毫秒数 默认500
        zkPro.put("operationTimeoutMilliseconds", 500);
//        zkPro.put("digest", "");
        ClusterPersistRepositoryConfiguration zkConfig = new ClusterPersistRepositoryConfiguration("ZooKeeper", "sharding", "", zkPro);*/

        // 可选配置：Memory、Standalone、Cluster, 默认Memory
        ModeConfiguration modeConfiguration = new ModeConfiguration("Standalone", standaloneConfig, false);
        return modeConfiguration;
    }

    private static Map<String, DataSource> buildDataSource() {
        /* 数据源配置 */
        DruidDataSource ds1 = new DruidDataSource();
        ds1.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds1.setUrl("jdbc:mysql://192.168.32.110:3306/sharding-jdbc?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8");
        ds1.setUsername("root");
        ds1.setPassword("Apelx123.");
        DruidDataSource ds2 = new DruidDataSource();
        ds2.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds2.setUrl("jdbc:mysql://192.168.32.111:3306/sharding-jdbc?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8");
        ds2.setUsername("root");
        ds2.setPassword("Apelx123.");

        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("ds_0", ds1);
        dataSourceMap.put("ds_1", ds2);
        return dataSourceMap;
    }

    private static Collection<RuleConfiguration> buildRule() {

        // ----------------------------------分片表规则配置----------------------------------
        // actualDataNodes: 由数据源名+表名组成，以小数点分隔。多个表以逗号分隔，支持行表达式
        ShardingTableRuleConfiguration orderTableRule = new ShardingTableRuleConfiguration("t_order",
                "ds_${0..1}.t_order_${[0, 1]}");

        /**
         * 当前表的分库策略, 如下:
         * 标准分片策略配置(StandardShardingStrategyConfiguration)
         *              shardingColumn: 分片列名称; shardingAlgorithmName: 分片算法名称(自定义的，然后在properties中设置)
         * 复合分片策略配置(ComplexShardingStrategyConfiguration)
         *               shardingColumns: 分片列名称，多个列以逗号分隔; shardingAlgorithmName: 分片算法名称
         * Hint 分片策略配置(HintShardingStrategyConfiguration)
         *              shardingAlgorithmName: 分片算法名称
         * 不分片策略配置 NoneShardingStrategyConfiguration
         */
        orderTableRule.setDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("customer_id", "tOrderDbShardingAlgorithm"));
        /**
         * 当前表的分表策略, 与分库一样的
         */
        orderTableRule.setTableShardingStrategy(new StandardShardingStrategyConfiguration("id", "tOrderTableShardingAlgorithm"));


        ShardingRuleConfiguration ruleConfiguration = new ShardingRuleConfiguration();
        // 添加自定义的order表规则
        ruleConfiguration.getTables().add(orderTableRule);

        // 添加自定义的order表分库算法
        Properties tOrderDbProp = new Properties();
        tOrderDbProp.setProperty("algorithm-expression", "ds_${customer_id % 2}");
        ruleConfiguration.getShardingAlgorithms().put("tOrderDbShardingAlgorithm", new ShardingSphereAlgorithmConfiguration("INLINE", tOrderDbProp));
        // 添加自定义的order表分表算法
        Properties tOrderTableProp = new Properties();
        tOrderTableProp.setProperty("algorithm-expression", "t_order_${id % 2 }");
        ruleConfiguration.getShardingAlgorithms().put("tOrderTableShardingAlgorithm", new ShardingSphereAlgorithmConfiguration("INLINE", tOrderTableProp));

        return Arrays.asList(ruleConfiguration);
    }


    private static void 官方api() throws SQLException {
        /* 数据源配置 */
        HikariDataSource writeDataSource0 = new HikariDataSource();
        writeDataSource0.setDriverClassName("com.mysql.jdbc.Driver");
        writeDataSource0.setJdbcUrl("jdbc:mysql://localhost:3306/db0?serverTimezone=UTC& useSSL=false&useUnicode=true&characterEncoding=UTF-8");
        writeDataSource0.setUsername("root");
        writeDataSource0.setPassword("");
        HikariDataSource writeDataSource1 = new HikariDataSource();
        // ... 忽略其他数据库配置项
        HikariDataSource read0OfwriteDataSource0 = new HikariDataSource();
        // ... 忽略其他数据库配置项
        HikariDataSource read1OfwriteDataSource0 = new HikariDataSource();
        // ... 忽略其他数据库配置项
        HikariDataSource read0OfwriteDataSource1 = new HikariDataSource();
        // ... 忽略其他数据库配置项
        HikariDataSource read1OfwriteDataSource1 = new HikariDataSource();
        // ... 忽略其他数据库配置项
        Map<String, DataSource> datasourceMaps = new HashMap<>(6);
        datasourceMaps.put("write_ds0", writeDataSource0);
        datasourceMaps.put("write_ds0_read0", read0OfwriteDataSource0);
        datasourceMaps.put("write_ds0_read1", read1OfwriteDataSource0);
        datasourceMaps.put("write_ds1", writeDataSource1);
        datasourceMaps.put("write_ds1_read0", read0OfwriteDataSource1);
        datasourceMaps.put("write_ds1_read1", read1OfwriteDataSource1);
        /** -----------------------------------------------------分片规则配置 -----------------------------------------------------*/
        // 表达式 ds_${0..1} 枚举值表示的是主从配置的逻辑数据源名称列表
        ShardingTableRuleConfiguration tOrderRuleConfiguration = new
                ShardingTableRuleConfiguration("t_order", "ds_${0..1}.t_order_${[0, 1]}");
        tOrderRuleConfiguration.setKeyGenerateStrategy(new
                KeyGenerateStrategyConfiguration("order_id", "snowflake"));

        tOrderRuleConfiguration.setTableShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "tOrderInlineShardingAlgorithm"));
        Properties tOrderShardingInlineProps = new Properties();
        tOrderShardingInlineProps.setProperty("algorithm-expression", "t_order_${order_id % 2 }");
        // 方法被删掉了!在ShardingRuleConfiguration类中去添加算法  tOrderRuleConfiguration.getShardingAlgorithms().putIfAbsent("tOrderInlineShardingAlgorithm", new ShardingSphereAlgorithmConfiguration("INLINE", tOrderShardingInlineProps));

        ShardingTableRuleConfiguration tOrderItemRuleConfiguration = new ShardingTableRuleConfiguration("t_order_item", "ds_${0..1}.t_order_item_${[0, 1]}");
        tOrderItemRuleConfiguration.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("order_item_id", "snowflake"));
        tOrderItemRuleConfiguration.setTableShardingStrategy(new StandardShardingStrategyConfiguration("order_item_id", "tOrderItemInlineShardingAlgorithm"));
        Properties tOrderItemShardingInlineProps = new Properties();
        tOrderItemShardingInlineProps.setProperty("algorithm-expression", "t_order_item_${order_item_id % 2}");
        // 方法被删掉了!在ShardingRuleConfiguration类中去添加算法 tOrderRuleConfiguration.getShardingAlgorithms().putIfAbsent("tOrderItemInlineShardingAlgorithm", new ShardingSphereAlgorithmConfiguration("INLINE", tOrderItemShardingInlineProps));

        ShardingRuleConfiguration shardingRuleConfiguration = new ShardingRuleConfiguration();
        shardingRuleConfiguration.getTables().add(tOrderRuleConfiguration);
        shardingRuleConfiguration.getTables().add(tOrderItemRuleConfiguration);
        shardingRuleConfiguration.getBindingTableGroups().add("t_order, t_order_item");
        shardingRuleConfiguration.getBroadcastTables().add("t_bank");

        // 上面被删掉的方法，在这里实现：
        shardingRuleConfiguration.getShardingAlgorithms().put("tOrderInlineShardingAlgorithm", new ShardingSphereAlgorithmConfiguration("INLINE", tOrderShardingInlineProps));
        shardingRuleConfiguration.getShardingAlgorithms().put("tOrderItemInlineShardingAlgorithm", new ShardingSphereAlgorithmConfiguration("INLINE", tOrderItemShardingInlineProps));

        // 默认分库策略
        shardingRuleConfiguration.setDefaultDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("user_id", "default_db_strategy_inline"));
        Properties defaultDatabaseStrategyInlineProps = new Properties();
        defaultDatabaseStrategyInlineProps.setProperty("algorithm-expression", "ds_${user_id % 2}");
        shardingRuleConfiguration.getShardingAlgorithms().put("default_db_strategy_inline", new ShardingSphereAlgorithmConfiguration("INLINE", defaultDatabaseStrategyInlineProps));
        // 分布式序列算法配置
        Properties snowflakeProperties = new Properties();
        shardingRuleConfiguration.getKeyGenerators().put("snowflake", new ShardingSphereAlgorithmConfiguration("SNOWFLAKE", snowflakeProperties));

        /** ---------------------------------------------------数据加密规则配置--------------------------------------------------- **/
        Properties encryptProperties = new Properties();
        encryptProperties.setProperty("aes-key-value", "123456");
        EncryptColumnRuleConfiguration columnConfigAes = new EncryptColumnRuleConfiguration("username", "username", "", "username_plain", "name_encryptor");
        EncryptColumnRuleConfiguration columnConfigTest = new EncryptColumnRuleConfiguration("pwd", "pwd", "assisted_query_pwd", "", "pwd_encryptor");
        EncryptTableRuleConfiguration encryptTableRuleConfig = new EncryptTableRuleConfiguration("t_user", Arrays.asList(columnConfigAes, columnConfigTest), true);
        Map<String, ShardingSphereAlgorithmConfiguration> encryptAlgorithmConfigs = new LinkedHashMap<>(2, 1);
        encryptAlgorithmConfigs.put("name_encryptor", new ShardingSphereAlgorithmConfiguration("AES", encryptProperties));
        encryptAlgorithmConfigs.put("pwd_encryptor", new ShardingSphereAlgorithmConfiguration("assistedTest", encryptProperties));
        EncryptRuleConfiguration encryptRuleConfiguration = new EncryptRuleConfiguration(Collections.singleton(encryptTableRuleConfig), encryptAlgorithmConfigs);

        /** ---------------------------------------------------读写分离规则配置--------------------------------------------------- **/
        Properties readwriteProps1 = new Properties();
        readwriteProps1.setProperty("write-data-source-name", "write_ds0");
        readwriteProps1.setProperty("read-data-source-names", "write_ds0_read0, write_ds0_read1");
        ReadwriteSplittingDataSourceRuleConfiguration dataSourceConfiguration1 = new ReadwriteSplittingDataSourceRuleConfiguration("ds_0", "Static", readwriteProps1, "roundRobin");
        Properties readwriteProps2 = new Properties();
        readwriteProps2.setProperty("write-data-source-name", "write_ds0");
        readwriteProps2.setProperty("read-data-source-names", "write_ds1_read0, write_ds1_read1");
        ReadwriteSplittingDataSourceRuleConfiguration dataSourceConfiguration2 = new ReadwriteSplittingDataSourceRuleConfiguration("ds_1", "Static", readwriteProps2, "roundRobin");
        // 负载均衡算法
        Map<String, ShardingSphereAlgorithmConfiguration> loadBalanceMaps = new HashMap<>(1);
        loadBalanceMaps.put("roundRobin", new ShardingSphereAlgorithmConfiguration("ROUND_ROBIN", new Properties()));
        ReadwriteSplittingRuleConfiguration readWriteSplittingyRuleConfiguration = new ReadwriteSplittingRuleConfiguration(Arrays.asList(dataSourceConfiguration1, dataSourceConfiguration2), loadBalanceMaps);
        /* 其他配置 */
        Properties otherProperties = new Properties();

        otherProperties.setProperty("sql-show", "true");
        /* shardingDataSource 就是最终被 ORM 框架或其他 jdbc 框架引用的数据源名称 */
        DataSource shardingDataSource = ShardingSphereDataSourceFactory.createDataSource(datasourceMaps, Arrays.asList(shardingRuleConfiguration, readWriteSplittingyRuleConfiguration, encryptRuleConfiguration), otherProperties);
    }
}